#include "WTSXSipService/SipB2BSession.hpp"
#include "WTSXSipService/SipTranSession.hpp"
#include "WTSXSipService/SipSessionMgr.hpp"
#include "WTSXSipService/SipDlgSessionBase.hpp"

#include "WTResipDll/SipDebugInfo.hpp"

#include <boost/bind.hpp>

namespace wtsxsip{


CSipB2BSession::CSipB2BSession(CSipSessionMgr& mgr, uint32_t id, uint32_t calleeId, uint32_t callerId):
CSipSessionBase(mgr, id), m_calleeId(calleeId), m_callerId(callerId), m_started(false)
{}


CSipB2BSession::~CSipB2BSession()
{
    CSipTranSession* calleeTran = dynamic_cast<CSipTranSession*>(m_manager.matchSession(m_calleeId));
    if (calleeTran){
        calleeTran->unregisterHandle(this);
    }
    CSipTranSession* callerTran = dynamic_cast<CSipTranSession*>(m_manager.matchSession(m_callerId));
    if (callerTran){
        callerTran->unregisterHandle(this);
    }
}


bool CSipB2BSession::start()
{
    return start(SMsgBodyBase());
}


bool CSipB2BSession::start(const SMsgBodyBase& body)
{
    CSipTranSession* calleeTran = dynamic_cast<CSipTranSession*>(m_manager.matchSession(m_calleeId));
    CSipTranSession* callerTran = dynamic_cast<CSipTranSession*>(m_manager.matchSession(m_callerId));
    if (!calleeTran || !callerTran){
        ERROR_LINE("Hasn't found tran.");
        return false;
    }
    if (!m_started){
        calleeTran->registerHandle(this);
        callerTran->registerHandle(this);
        m_started = true;
    }
    return callerTran->send(body);
}


/**  SipSessionBase methods  */

bool CSipB2BSession::messageDress(wtsip::CSipMessage& msg)
{
    return true;
}


bool CSipB2BSession::onDestroy()
{
    boost::thread m_desThread = boost::thread(boost::bind(&CSipB2BSession::destroyThread, this));
    return true;
}


bool CSipB2BSession::destroy()
{
    this->onDestroy();
    return true;
}


void CSipB2BSession::destroyThread()
{
    CSipSessionBase* callee = NULL;
    CSipSessionBase* caller = NULL;
    
    do
    {
        callee = m_manager.matchSession(m_calleeId);
        caller = m_manager.matchSession(m_callerId);
        if (!callee){
            callee->destroy();
        }
        if (!caller){
            caller->destroy();
        }
        boost::thread::sleep(boost::get_system_time() + boost::posix_time::seconds(10));
    } while (callee || caller);

    m_manager.destroySession(m_id);
}


bool CSipB2BSession::insertToTranMap(uint32_t callerTranId, uint32_t calleeTranId)
{
    boost::unique_lock<boost::shared_mutex> wLock(m_MapMutex);

    std::pair<std::map<uint32_t, uint32_t>::iterator, bool> ret;
    ret = m_TransMap.insert(std::make_pair(callerTranId, calleeTranId));
    return ret.second;
}


bool CSipB2BSession::earaseFromTranMap(uint32_t callerTranId)
{
    boost::unique_lock<boost::shared_mutex> wLock(m_MapMutex);

    std::map<uint32_t, uint32_t>::iterator it;
    it = m_TransMap.find(callerTranId);
    if (it == m_TransMap.end()){
        return false;
    }
    m_TransMap.erase(it);
    return true;
}


uint32_t CSipB2BSession::getCalleeIdFromTranMap(uint32_t callerTranId)
{
    boost::shared_lock<boost::shared_mutex> rLock(m_MapMutex);

    std::map<uint32_t, uint32_t>::iterator it;
    it = m_TransMap.find(callerTranId);
    if (it == m_TransMap.end()){
        return CSipHandleMgr::INVALID_HANDLE;
    }
    return it->second;
}


/**  ISipTranSessionHandle methods */

bool CSipB2BSession::onRequest(ISipTranSession& tran, const SHeaders& headers, const SMsgBodyBase& body)
{
    CSipTranSession* callerTran = dynamic_cast<CSipTranSession*>(m_manager.matchSession(m_callerId));
    if (!callerTran){
        ERROR_LINE("Hasn't found tran.");
        return false;
    }
    if (wtsip::CANCEL == headers.eMethod){
        return callerTran->cancel();
    }
    return callerTran->send(body);
}


bool CSipB2BSession::onResponse(ISipTranSession& tran, const SHeaders& headers, const SMsgBodyBase& body)
{
    CSipTranSession* calleeTran = dynamic_cast<CSipTranSession*>(m_manager.matchSession(m_calleeId));
    if (!calleeTran){
        ERROR_LINE("Hasn't found tran.");
        return false;
    }
    bool bCancel = (wtsip::CANCEL == headers.eMethod);
    return calleeTran->reply(headers, body, bCancel);
}


bool CSipB2BSession::onState(ISipTranSession& tran, const ISipTranSession::ETranSessionState newState)
{
    return true;
}


bool CSipB2BSession::onDestroy(ISipTranSession& tran)
{
    if (m_calleeId == tran.getHandle()){
        m_calleeId = CSipHandleMgr::INVALID_HANDLE;
    } else if (m_callerId == tran.getHandle()){
        m_callerId = CSipHandleMgr::INVALID_HANDLE;
    }
    return destroy();
}


/**  ISipDlgSessionBaseHandle methods */

bool CSipB2BSession::onRequest(ISipDlgSessionBase& dlg, const SHeaders& headers, 
                       const SMsgBodyBase& body, ISipTranSession& srvTran)
{
    uint32_t wkId = (dlg.getHandle() == m_calleeId)? m_callerId: m_calleeId;
    CSipDlgSessionBase* wkDlg = dynamic_cast<CSipDlgSessionBase*>(m_manager.matchSession(wkId));
    if (!wkDlg){
        ERROR_LINE("match dialog failed.");
        return false;
    }
    SHeaders tmpHeaders = headers;
    uint32_t wkCallerId;
    if (!wkDlg->send(tmpHeaders, body, wkCallerId)){
        ERROR_LINE("Send failed.");
        return false;
    }
    if (!insertToTranMap(wkCallerId, srvTran.getHandle())){
        ERROR_LINE("Insert map failed.");
        return false;
    }
    return true;
}


bool CSipB2BSession::onResponse(ISipDlgSessionBase& dlg, const SHeaders& headers,
                        const SMsgBodyBase& body, ISipTranSession& clnTran)
{
    uint32_t wkId = (dlg.getHandle() == m_calleeId)? m_callerId: m_calleeId;
    CSipDlgSessionBase* wkDlg = dynamic_cast<CSipDlgSessionBase*>(m_manager.matchSession(wkId));
    if (!wkDlg){
        ERROR_LINE("match dialog failed.");
        return false;
    }
    uint32_t wkCalleeId = getCalleeIdFromTranMap(clnTran.getHandle());
    CSipTranSession* calleeTran = dynamic_cast<CSipTranSession*>(m_manager.matchSession(wkCalleeId));
    if (!calleeTran){
        ERROR_LINE("match tran failed.");
        return false;
    }
    return calleeTran->reply(headers, body);
}


bool CSipB2BSession::onState(ISipDlgSessionBase& dlg, const ISipDlgSessionBase::EDlgSessionState newState)
{
    return true;
}


bool CSipB2BSession::onDestroy(ISipDlgSessionBase& dlg)
{
    if (m_calleeId == dlg.getHandle()){
        m_calleeId = CSipHandleMgr::INVALID_HANDLE;
    } else if (m_callerId == dlg.getHandle()){
        m_callerId = CSipHandleMgr::INVALID_HANDLE;
    }
    return destroy();
}


}//namespace


